package com.fundamental.lock;

import com.sun.corba.se.impl.orbutil.concurrent.Sync;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.locks.AbstractOwnableSynchronizer;

/**
 * 独占锁，需要给等待或非等待锁，抢占或非抢占锁，公平与不公平锁，留下空间
 *
 * 涉及到的几个策略
 *
 * 判断是否获得锁（是否要进行锁等待）-------锁等待策略（阻塞或忙等，等待数据结构）--------释放锁策略（选择线程释放策略，交接锁策略）--------取消情况处理。
 *
 *
 *
 * 涉及到的几个并发冲突点
 *
 1.判断与判断（包括释放后的判断）之间的竞争。Aqs通过一个尾部原子操作避免竞争
 2.取消和取消之间的竞争。Prev避免竞争
 3.判断和取消之间的竞争。Prev避免竞争，尾部原子操作
 4.判断和释放之间的竞争。首尾不相关
 5.取消与释放之间的竞争。Prev和head避免竞争
 *
 */
public abstract class AbstractExclusiveSync extends AbstractOwnableSynchronizer implements Sync {

    private static final Unsafe unsafe;

    private static final int SPIN_COUNT_LIMIT = 3;

    private static final long stateOffset;
    private static final long spinCountOffset;
    private static final long headOffset;
    private static final long tailOffset;

    static {
        try {

            Class<Unsafe> clazz = Unsafe.class;
            Field field = clazz.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);

            stateOffset = unsafe.objectFieldOffset(AbstractExclusiveSync.class.getDeclaredField("state"));
            spinCountOffset = unsafe.objectFieldOffset(AbstractExclusiveSync.class.getDeclaredField("spinCount"));
            headOffset = unsafe.objectFieldOffset(AbstractExclusiveSync.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset(AbstractExclusiveSync.class.getDeclaredField("tail"));
        }catch (Exception e){
            throw new Error(e);
        }
    }

    private static class Node{
        Node prev;
        Node next;
        int status;
        Thread thread;

        Node(){

        }

        Node(Thread thread){
            this.thread = thread;
        }

        class Status{

            final int init = 0;
            final int readyToBlock = 1;

        }
    }

    //剩余几个锁，用来标志当前锁的状态，状态为1代表当前无线程持有该锁，为0代表该锁已被占用。
    protected volatile int state;
    //当前自旋获得锁的数量
    protected volatile int spinCount;
    //阻塞队列头
    protected volatile Node head;
    //阻塞队列尾
    protected volatile Node tail;

    public boolean tryAcquire(int count){
        return false;
    }

    @Override
    public void acquire() throws InterruptedException {
        //尝试直接获得锁，进行  锁获取判断策略 和 抢占策略。
        if(!tryAcquire(1)){
            //失败的话  进入    线程等待策略
            tryDoWait(1);
        }
    }

    private void tryDoWait(int count) {

        int spinCount = getSpinCount();
        //当前的自旋数量较少时，自旋。带来的问题有：1.效率，2.公平性。公平锁是不能这么实现的。
        if(spinCount < SPIN_COUNT_LIMIT && compareAndSetSpinCount(spinCount,spinCount + 1)){
            spin(1);
            return;
        }
        //其他情况下进入阻塞队列并阻塞。
        //要解决的问题：判断与判断  判断与释放， 判断与取消 线程间的竞争。
        //加入阻塞队列的竞争。

        //1.阻塞队列的数据结构选择
        //该阻塞队列，需要提供  加入操作，任意位置删除操作，顺序结构。
        //
        //因此，链表是最好的选择
        //判断的线程会去访问这个链表，进行加入操作，竞争在尾部。
        //释放的线程会去访问这个链表，释放头部节点。在释放时，如果头部节点和尾部节点是同一个节点且去执行移出操作，则有竞争。
        //取消的线程会去访问这个链表，把当前节点的状态设置为取消，取消时只要不是取消头部节点或者尾部节点，就能在取消后的移出操作中不引入竞争。

        //2.阻塞队列的基本操作，和减少竞争的策略。
        //总体而言，加入尾部，释放头部，取消设置，取消移除，这四个操作一定得是没有竞争的，具体放在判断线程，释放线程还是取消线程，是又一个策略。

        //这里仿照aqs的做法，令尾部竞争，头部竞争和所有取消移除都不会同时发生。

        Node addRes = addWaitingNode();

        try{
            while(true){

                //判断是否需要阻塞当前线程，如果需要则阻塞。
               /* if(){


                }
*/


            }

        }/*catch (InterruptedException e){

        }*/finally {

        }



    }

    private Node addWaitingNode() {

        Node newNode = new Node(Thread.currentThread());

        while(true) {
            Node nowTail = tail;
            if (tail == null) {
                //如果当前没有被初始化过。则进行初始化，只有一个能初始化成功,
                //注意，这里是不能先settail的，因为前面的判定条件是判定tail是否为null去进行下一步的，
                // ，所以在tail必须放在最后一步设置，否则其他线程判断tail不为null，在head还没初始化的情况下就执行下一步了。
           /* if(compareAndSetTail(null,newNode)){
                head
            }else{

            }*/
                //这里为head维护一个哨兵Node的意义在于，当更换head时，只需要将当前节点赋到head即可，不用考虑当前节点的next是否为空。如果要考虑当前节点的next是否为空，则会与加入节点产生竞争。
                if(compareAndSetHead(null,new Node())){
                    tail = head;
                }
            } else {
                //注意，prev一定要在入队前设置，因为设计中，一旦入队，那么prev一定要保证正确，而next未必。
                newNode.prev = nowTail;
                if(compareAndSetTail(nowTail,newNode)){
                    nowTail.next = newNode;
                    return newNode;
                }
            }
        }

    }

    private void spin(int count){

        while(!compareAndSetState(1,0)) {

        }
        getAndAddSpinCount(-1);
    }

    private boolean compareAndSetState(int expect,int update){
        return unsafe.compareAndSwapInt(this,stateOffset,expect,update);
    }

    private boolean compareAndSetSpinCount(int expect, int update){
        return unsafe.compareAndSwapInt(this,spinCountOffset,expect,update);
    }

    private boolean compareAndSetHead(Node expect,Node update){
        return unsafe.compareAndSwapObject(this,headOffset,expect,update);
    }

    private boolean compareAndSetTail(Node expect,Node update){
        return unsafe.compareAndSwapObject(this,tailOffset,expect,update);
    }

    private void getAndAddSpinCount(int add){
        unsafe.getAndAddInt(this,spinCountOffset,add);
    }

    @Override
    public boolean attempt(long msecs) throws InterruptedException {
        return false;
    }

    @Override
    public void release() {

    }

    public int getSpinCount() {
        return spinCount;
    }
}
